﻿

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;


using MySqlConnector;

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

using Utils;

namespace MicroDb.Mysql
{
    class MySqlExecute : SqlExecute
    {

        private readonly ILogger logger;

        private readonly string connectionString;

        public MySqlExecute(ILogger<MySqlExecute> logger, string connectionString)
        {
            this.logger = logger;
            this.connectionString = connectionString;
        }

        protected override DbConnection CreateDbConnection()
        {
            return new MySqlConnection(connectionString);
        }

        protected override DbParameter[] CreateParameters(IDictionary<string, object> parameters)
        {
            return parameters.Select(a => new MySqlParameter(a.Key, a.Value)).ToArray();
        }

        /// <summary>
        /// 扩展方法
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="obj"></param>
        /// <param name="paras"></param>
        /// <param name="changeValue"></param>
        /// <returns></returns>
        public override string CustomMethod(string methodName, Expression obj, IReadOnlyList<Expression> arguments, Func<Expression, string> changeValue)
        {
            switch (methodName)
            {
                case "ToDate":
                    return string.Concat(" STR_TO_DATE(", changeValue(arguments[0]), ", '%Y%m%d') ");
                case "ToInt":
                case "DateToInt":
                    return string.Concat(" CAST( DATE_FORMAT(", changeValue(arguments[0]), ", '%Y%m%d') ,UNSIGNED INTEGER)");
                case "UnixTimestamp":
                    return string.Concat("UNIX_TIMESTAMP(", changeValue(arguments[0]), ")");
                case "Floor":
                case "Round":
                case "Ceiling":
                    return string.Concat(methodName.ToUpper(), "(", changeValue(arguments[0]), ")");
                case "AddYears":
                case "AddMonths":
                case "AddDays":
                case "AddHours":
                case "AddMinutes":
                case "AddSeconds":
                    return $"DATE_ADD({changeValue(obj)},interval {changeValue(arguments[0])} {methodName[3..].TrimEnd('s').ToLower()})";
                default:
                    break;
            }
            return base.CustomMethod(methodName, obj, arguments, changeValue);
        }

        public override void Logs(string commandText, IDictionary<string, object> parameters)
        {
            if (logger.IsEnabled(LogLevel.Debug))
            {
                if (parameters != null)
                    foreach (var item in parameters.OrderByDescending(a => a.Key))
                    {
                        commandText = commandText.Replace(item.Key, GetValue(item.Value));
                    }
                logger.LogDebug(commandText);
            }
        }

        public override bool InsertFilter(string propertyName, ref object value)
        {
            if (propertyName == "CreateTime" && (DateTime.MinValue.Equals(value) || (value is Nullable<DateTime> && value == null)))
            {
                value = DateTime.Now;
            }
            if (propertyName == "CreateDate" && 0.Equals(value))
            {
                value = DateTime.Now.ToInt();
            }
            if (propertyName == "UpdateTime" && (DateTime.MinValue.Equals(value) || (value is DateTime? && value == null)))
            {
                value = DateTime.Now;
            }
            if (propertyName == "UpdateTime" && 0.Equals(value))
            {
                value = DateTime.Now.ToInt();
            }
            return true;
        }

        public override bool UpdateFilter(string propertyName, ref object value)
        {
            if (propertyName == "UpdateTime" && (DateTime.MinValue.Equals(value) || (value is DateTime? && value == null)))
            {
                value = DateTime.Now;
            }
            if (propertyName == "UpdateTime" && 0.Equals(value))
            {
                value = DateTime.Now.ToInt();
            }
            return true;
        }

        private string GetValue(object obj)
        {
            if (obj == null)
            {
                return "NULL";
            }
            var typeName = obj.GetType().Name;
            switch (typeName)
            {
                case "String":
                case "Char":
                    return $"'{obj}'";
                case "DateTime":
                    return $"'{((DateTime)obj):yyyy-MM-dd HH:mm:ss}'";
                case "Boolean":
                    return $"{(((Boolean)obj) ? 1 : 0)}";
                default:
                    return obj.ToString();
            }
        }


    }
}
